---
title: "10. Authenticate your operations"
---

In this section, you will book a flight 🚀! Booking a flight requires being authenticated to the server so the correct person is sent to space! To do that, and since Apollo Kotlin is using [OkHttp](https://square.github.io/okhttp/) to handle HTTP requests, you will use an OkHttp [Interceptor](https://square.github.io/okhttp/interceptors/) to add headers to your GraphQL requests.

## Add the interceptor

In `Apollo.kt`, add the `AuthorizationInterceptor` class:

```kotlin title="app/src/main/kotlin/com/example/rocketreserver/Apollo.kt"
private class AuthorizationInterceptor() : Interceptor {
  override fun intercept(chain: Interceptor.Chain): Response {
    val request = chain.request().newBuilder()
        .apply {
          TokenRepository.getToken()?.let { token ->
            addHeader("Authorization", token)
          }
        }
        .build()
    return chain.proceed(request)
  }
}
```

This interceptor appends an `"Authorization: $token"` HTTP header to requests if the token is not null.

## Use the interceptor

Create a custom `OkHttpClient` that will use this interceptor and pass it to the `ApolloClient`:

```kotlin title="app/src/main/kotlin/com/example/rocketreserver/Apollo.kt"
val apolloClient = ApolloClient.Builder()
    .serverUrl("https://apollo-fullstack-tutorial.herokuapp.com/graphql")
    .okHttpClient( // highlight-line
        OkHttpClient.Builder() // highlight-line
            .addInterceptor(AuthorizationInterceptor()) // highlight-line
            .build() // highlight-line
    ) // highlight-line
    .build()
```

## Add the BookTrip and CancelTrip mutations

Next to `schema.graphqls` add a `BookTrip.graphql` file:

```graphql title="app/src/main/graphql/BookTrip.graphql"
mutation BookTrip($id:ID!) {
  bookTrips(launchIds: [$id]) {
    success
    message
  }
}
```

Notice how the `bookTrips` field takes a list as argument but the mutation itself only take a single variable.

Also add the `CancelTrip.graphql` file. This mutation doesn't use lists:

```graphql title="app/src/main/graphql/CancelTrip.graphql"
mutation CancelTrip($id:ID!) {
  cancelTrip(launchId: $id) {
    success
    message
  }
}
```

## Connect the mutations to your UI

Go back to `LaunchDetails.kt`, and replace the `TODO`s in `onBookButtonClick` by executing the appropriate mutation based on whether the launch is booked: 

```kotlin title="app/src/main/java/com/example/rocketreserver/LaunchDetails.kt"
private suspend fun onBookButtonClick(launchId: String, isBooked: Boolean, navigateToLogin: () -> Unit): Boolean {
    if (TokenRepository.getToken() == null) {
        navigateToLogin()
        return false
    }
    val mutation = if (isBooked) {
        CancelTripMutation(id = launchId)
    } else {
        BookTripMutation(id = launchId)
    }
    val response = apolloClient.mutation(mutation).execute()
    return when {
        response.exception != null -> {
            Log.w("LaunchDetails", "Failed to book/cancel trip", response.exception)
            false
        }
    
        response.hasErrors() -> {
            Log.w("LaunchDetails", "Failed to book/cancel trip: ${response.errors?.get(0)?.message}")
            false
        }
    
        else -> true
    }
}
```

Now back to the `LaunchDetails` function, declare a coroutine scope to be able to call the suspend `onBookButtonClick`.
Also, let's remember `isBooked` and change the button's text accordingly:

```kotlin title="app/src/main/java/com/example/rocketreserver/LaunchDetails.kt"
// Book button
val scope = rememberCoroutineScope() // highlight-line
var isBooked by remember { mutableStateOf(data.launch?.isBooked == true) } // highlight-line
Button(
    modifier = Modifier
        .padding(top = 32.dp)
        .fillMaxWidth(),
    onClick = {
        scope.launch {
            val ok = onBookButtonClick( // highlight-line
                launchId = data.launch?.id ?: "", // highlight-line
                isBooked = isBooked, // highlight-line
                navigateToLogin = navigateToLogin // highlight-line
            ) // highlight-line
            if (ok) { // highlight-line
                isBooked = !isBooked // highlight-line
            } // highlight-line
        }
    }
) {
    Text(text = if (!isBooked) "Book now" else "Cancel booking") // highlight-line
}
```

Let's also add a loading indicator and prevent the button from being clicked while the mutation is running:

```kotlin title="app/src/main/java/com/example/rocketreserver/LaunchDetails.kt"
// Book button
var loading by remember { mutableStateOf(false) } // highlight-line
val scope = rememberCoroutineScope()
var isBooked by remember { mutableStateOf(data.launch?.isBooked == true) }
Button(
    modifier = Modifier
        .padding(top = 32.dp)
        .fillMaxWidth(),
    enabled = !loading, // highlight-line
    onClick = {
        loading = true // highlight-line
        scope.launch {
            val ok = onBookButtonClick(
                launchId = data.launch?.id ?: "",
                isBooked = isBooked,
                navigateToLogin = navigateToLogin
            )
            if (ok) {
                isBooked = !isBooked
            }
            loading = false // highlight-line
        }
    }
) {
    if (loading) { // highlight-line
        SmallLoading() // highlight-line
    } else { // highlight-line
        Text(text = if (!isBooked) "Book now" else "Cancel booking")
    } // highlight-line
}
```

## Book your trip!

Compile and run your app. You can now book and cancel your trips! The button will change based on whether a trip has been booked or not.

In the next section, you will [write your first subscription](11-subscriptions) and be notified in real time when someone books a flight.
